package com.zdp.leetcodeMiddle;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicIntegerArray;

public class 哲学家进餐 {
    /*
    * 题目描述：
    *   5 个沉默寡言的哲学家围坐在圆桌前，每人面前一盘意面。叉子放在哲学家之间的桌面上。（5 个哲学家，5 根叉子）

    所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面，
    * 而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。
    * 只要条件允许，哲学家可以拿起左边或者右边的叉子，但在没有同时拿到左右叉子时不能进食。
    假设面的数量没有限制，哲学家也能随便吃，不需要考虑吃不吃得下。
    设计一个进餐规则（并行算法）使得每个哲学家都不会挨饿；也就是说，在没有人知道别人什么时候想吃东西或思考的情况下，
    * 每个哲学家都可以在吃饭和思考之间一直交替下去。
    哲学家从 0 到 4 按 顺时针 编号。
    * 请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork)：
    philosopher 哲学家的编号。
    pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
    eat 表示吃面。
    putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
    由于哲学家不是在吃面就是在想着啥时候吃面，所以思考这个方法没有对应的回调。
    给你 5 个线程，每个都代表一个哲学家，请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前，可能会为同一个哲学家多次调用该函数。
链接：https://leetcode-cn.com/problems/the-dining-philosophers
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
    * */
}
/*
* 使用信号量机制来进行
* 因为每个哲学家拿的筷子 都是固定的，所以这5支筷子应该是不同类型的资源
* 因此要设置五个信号量 来代表这五只筷子
* 哲学家 固定拿它左手和右手边的筷子 即 kuaizi[philosopher] kuaizi[(philosopher+1)%5]
* 但使用五个信号量 感觉耗费的空间比较多  用1个原子类型的数组应该就可以解决了
* */
class DiningPhilosophers {
    private Semaphore mutex;
    private Semaphore[] kuaizi ;
    public DiningPhilosophers() {
       kuaizi = new Semaphore[]{
               new Semaphore(1),
               new Semaphore(1),
               new Semaphore(1),
               new Semaphore(1),
               new Semaphore(1)
       };
        mutex = new Semaphore(1);
    }
    //五个哲学家都拿自己左手边的筷子 人手一只筷子，导致死锁
    //为了防止产生死锁，所以应用一个信号量将 拿筷子的操作包裹起来
    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        mutex.acquire();
        kuaizi[philosopher].acquire();
        kuaizi[(philosopher+1)%5].acquire();
        pickLeftFork.run();
        pickRightFork.run();
        mutex.release();
        eat.run();
        putLeftFork.run();
        putRightFork.run();
        kuaizi[philosopher].release();
        kuaizi[(philosopher+1)%5].release();

    }
}

class DiningPhilosophers1 {
    private Semaphore[] kuaizi ;
    public DiningPhilosophers1() {
        kuaizi = new Semaphore[]{
                new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1),
                new Semaphore(1)
        };

    }
   //防止死锁 ，让奇数号的哲学家拿右手边的，偶数号拿左手边的
    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {
        if(philosopher%2==0){
            kuaizi[philosopher].acquire();
            kuaizi[(philosopher+1)%5].acquire();
        }else{
            kuaizi[(philosopher+1)%5].acquire();
            kuaizi[philosopher].acquire();
        }
        pickLeftFork.run();
        pickRightFork.run();
        eat.run();
        putLeftFork.run();
        putRightFork.run();
        kuaizi[philosopher].release();
        kuaizi[(philosopher+1)%5].release();
    }
}

